package com.cc.dingtalk;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * @author CC
 * @version 1.0
 * @description: 钉钉机器人消息发送服务
 * @date 2025/10/14 15:02
 */
@Component
@Slf4j
public class SendDingService {

    @Value("${ding.robot.webhook:}")
    private String webhook;

    @Value("${ding.robot.secret:}")
    private String secret;

    @Autowired
    private RestTemplate restTemplate;
    // 消息类型常量
    private static final String MSG_TYPE_TEXT = "text";
    private static final String MSG_TYPE_LINK = "link";
    private static final String MSG_TYPE_MARKDOWN = "markdown";
    private static final String MSG_ACTION_CARD = "actionCard";
    private static final String MSG_FEED_CARD = "feedCard";

    // 钉钉API基础URL
    private static final String DING_API_BASE_URL = "https://oapi.dingtalk.com/robot/send";

    // 使用ThreadLocal存储请求参数
    private final ThreadLocal<SendRequestParam> paramLocal = new ThreadLocal<>();

    /**
     * 发送文本消息
     */
    public SendDingService sendText(SendRequestParam.Text text) {
        setRequestParam(MSG_TYPE_TEXT, text, null, null, null, null);
        return this;
    }

    /**
     * 发送链接消息
     */
    public SendDingService sendLink(SendRequestParam.Link link) {
        setRequestParam(MSG_TYPE_LINK, null, link, null, null, null);
        return this;
    }

    /**
     * 发送markdown消息
     */
    public SendDingService sendMarkdown(SendRequestParam.Markdown markdown) {
        setRequestParam(MSG_TYPE_MARKDOWN, null, null, markdown, null, null);
        return this;
    }

    /**
     * 发送ActionCard消息
     */
    public SendDingService sendActionCard(SendRequestParam.ActionCard actionCard) {
        setRequestParam(MSG_ACTION_CARD, null, null, null, actionCard, null);
        return this;
    }

    /**
     * 发送FeedCard消息
     */
    public SendDingService sendFeedCard(SendRequestParam.FeedCard feedCard) {
        setRequestParam(MSG_FEED_CARD, null, null, null, null, feedCard);
        return this;
    }

    /**
     * 设置请求参数
     */
    private void setRequestParam(String msgType, SendRequestParam.Text text,
                                 SendRequestParam.Link link, SendRequestParam.Markdown markdown,
                                 SendRequestParam.ActionCard actionCard, SendRequestParam.FeedCard feedCard) {
        SendRequestParam request = new SendRequestParam();
        request.setMsgtype(msgType);

        switch (msgType) {
            case MSG_TYPE_TEXT:
                request.setText(text);
                break;
            case MSG_TYPE_LINK:
                request.setLink(link);
                break;
            case MSG_TYPE_MARKDOWN:
                request.setMarkdown(markdown);
                break;
            case MSG_ACTION_CARD:
                request.setActionCard(actionCard);
                break;
            case MSG_FEED_CARD:
                request.setFeedCard(feedCard);
                break;
            default:
                throw new IllegalArgumentException("不支持的的消息类型: " + msgType);
        }

        paramLocal.set(request);
    }

    /**
     * 生成钉钉机器人URL（带签名）
     */
    private String getDingUrl() {
        if (webhook == null || webhook.trim().isEmpty()) {
            throw new IllegalStateException("钉钉机器人webhook未配置");
        }

        // 如果没有secret，直接使用webhook
        if (secret == null || secret.trim().isEmpty()) {
            log.warn("钉钉机器人secret未配置，使用无签名方式");
            return webhook;
        }

        try {
            Long timestamp = System.currentTimeMillis();
            String stringToSign = timestamp + "\n" + secret;

            // 生成HMAC-SHA256签名
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
            String sign = URLEncoder.encode(Base64.encodeBase64String(signData), StandardCharsets.UTF_8.name());

            // 构建带签名的URL
            return String.format("%s?access_token=%s&timestamp=%d&sign=%s",
                    DING_API_BASE_URL, extractAccessToken(webhook), timestamp, sign);

        } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
            log.error("生成钉钉URL签名失败", e);
            throw new RuntimeException("钉钉URL签名生成失败", e);
        }
    }

    /**
     * 从webhook中提取access_token
     */
    private String extractAccessToken(String webhookUrl) {
        // 如果webhook已经是完整URL，提取access_token参数
        if (webhookUrl.contains("access_token=")) {
            String[] parts = webhookUrl.split("access_token=");
            if (parts.length > 1) {
                return parts[1].split("&")[0];
            }
        }
        // 如果webhook只是access_token，直接返回
        return webhookUrl;
    }

    /**
     * 发送消息（不带@提醒）
     */
    public String send() {
        return this.send(null);
    }

    /**
     * 发送消息（带@提醒）
     */
    public String send(SendRequestParam.At at) {
        String json = null;
        String dingUrl = null;

        try {
            // 验证参数
            SendRequestParam param = paramLocal.get();
            if (param == null) {
                throw new IllegalStateException("未设置消息内容，请先调用消息类型方法");
            }

            dingUrl = this.getDingUrl();
            param.setAt(at);
            json = JSON.toJSONString(param);

            log.debug("准备发送钉钉消息，URL: {}, 参数: {}", dingUrl, json);
            return sendRequest(dingUrl, json);

        } catch (Exception e) {
            log.error("钉钉消息发送失败，接口地址:[{}]，请求参数:[{}]", dingUrl, json, e);
            throw new RuntimeException("钉钉消息发送失败: " + e.getMessage(), e);
        } finally {
            paramLocal.remove();
        }
    }

    /**
     * 发送HTTP请求
     */
    private String sendRequest(String url, String params) {
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<String> httpEntity = new HttpEntity<>(params, headers);

            ResponseEntity<String> entity = restTemplate.postForEntity(url, httpEntity, String.class);
            String body = entity.getBody();

            log.info("钉钉消息发送响应: {}", body);

            // 检查响应状态
            if (body != null) {
                if (body.contains("\"errcode\":0")) {
                    log.info("钉钉消息发送成功");
                } else if (body.contains("\"errcode\":40035")) {
                    log.error("钉钉机器人发送消息失败，缺少access_token参数，请检查webhook配置");
                } else if (body.contains("\"errcode\":310000")) {
                    log.error("钉钉机器人签名验证失败，请检查secret配置");
                }
            }

            return body;

        } catch (Exception e) {
            log.error("钉钉消息请求失败", e);
            throw new RuntimeException("钉钉消息请求失败: " + e.getMessage(), e);
        }
    }

    /**
     * 快速发送文本消息的便捷方法
     */
    public boolean sendQuickText(String content) {
        try {
            SendRequestParam.Text text = new SendRequestParam.Text();
            text.setContent(content);

            this.sendText(text).send();
            return true;
        } catch (Exception e) {
            log.error("快速发送文本消息失败", e);
            return false;
        }
    }
}